#include "sphere.h"
#include <QVector2D>
#include <QVector3D>
#include <QDebug>
#include<math.h>
#define PI 3.1415926//圆周率
#define ROUND(d) int(d + 0.5)
struct VertexData
{
    QVector3D position;
    QVector2D texCoord;
};
CSphere::CSphere(GLFuncName *func)
    : m_func(func)
    , m_indexBuf(QOpenGLBuffer::IndexBuffer)
    , m_vertexShader(QOpenGLShader::Vertex)
    , m_fragmentShader(QOpenGLShader::Fragment)
    , m_texture(QOpenGLTexture::Target2D)
{
    initBuffer();
    initShader();
    initTexture();
}
CSphere::~CSphere()
{
    m_arrayBuf.destroy();
    m_indexBuf.destroy();
    m_texture.destroy();
}
void CSphere::initBuffer()
{
    int nWidth = ROUND(2 * PI * r);//目的图像的宽度
    int nHeight = ROUND(PI * r);
    m_arrayBuf.create();
    m_indexBuf.create();
    Alpha = 4, Beta = 8;//面片夹角
    n1 = 180 / Alpha, n2 = 360 / Beta;
    VertexData vertices[1982];
    double Alpha1, Beta1;
    vertices->position.setX(0);vertices->position.setY(r);vertices->position.setZ(0);
    vertices->texCoord.setX(0);vertices->texCoord.setY(0);

    vertices->position.setX(0);vertices->position.setY(-r);vertices->position.setZ(0);
    vertices->texCoord.setX(0);vertices->texCoord.setY(0);
    for (int i = 0; i < n1 - 1; i++)
    {
        Alpha1 = (i + 1) * Alpha * PI / 180;
        for (int j = 0; j < n2; j++)
        {
            Beta1 = j * Beta * PI / 180;
            vertices[i * n2 + j + 1].position.setX(  r * sin(Alpha1) * sin(Beta1));
            vertices[i * n2 + j + 1].position.setY(  r * cos(Alpha1));
            vertices[i * n2 + j + 1].position.setZ( (r * sin(Alpha1) * cos(Beta1)));
            vertices[i * n2 + j + 1].texCoord.setX( Beta1 / (2 * PI) * (1.0/ ROUND(2 * PI * r)));
            vertices[i * n2 + j + 1].texCoord.setY(  (PI - Alpha1) / PI * (1.0/ ROUND(PI * r)));
        }
    }
    GLushort indices[11880];
    int k,n;
    for (int j = 0; j < n2; j++)//构造北极三角形面片
    {
        int tempj = j + 1;
        n=j;
        if (n2 == tempj)
            tempj = 0;//面片的首尾连接
        int NorthIndex[3];//北极三角形面片索引号数组

        NorthIndex[0] = 0;
        NorthIndex[1] = j + 1;
        NorthIndex[2] = tempj + 1;

        for ( k = 0; k < 3; k++){
            indices[n*3]=NorthIndex[0];
            indices[n*3+1]=NorthIndex[2];
            indices[n*3+2]=NorthIndex[1];
        }
    }
    int l=n2*3;
    for (int i = 1; i < n1 - 1; i++)//构造球面四边形面片
    {
        for (int j = 0; j < n2; j++)
        {
            n=j+n1*(i-1);
            int tempi = i + 1;
            int tempj = j + 1;
            if (n2 == tempj)
                tempj = 0;//面片的首尾连接
            int BodyIndex[4];//球面四边形面片索引号数组
            BodyIndex[0] = (i - 1) * n2 + j + 1;
            BodyIndex[1] = (tempi - 1) * n2 + j + 1;
            BodyIndex[2] = (tempi - 1) * n2 + tempj + 1;
            BodyIndex[3] = (i - 1) * n2 + tempj + 1;
            for (int k = 0; k < 4; k++)
                indices[n*6+l]=BodyIndex[0];
            indices[n*6+1+l]=BodyIndex[1];
            indices[n*6+2+l]=BodyIndex[2];
            indices[n*6+3+l]=BodyIndex[1];
            indices[n*6+4+l]=BodyIndex[3];
            indices[n*6+5+l]=BodyIndex[2];
        }
    }
    l=l+(n1-2)*n2*6;
    for (int j = 0; j < n2; j++)//构造南极三角形面片
    {
        n=j;
        int tempj = j + 1;
        if (n2 == tempj)
            tempj = 0;//面片的首尾连接
        int SouthIndex[3];//南极三角形面片索引号数组
        SouthIndex[0] = (n1 - 2) * n2 + j + 1;
        SouthIndex[1] = (n1 - 1) * n2 + 1;
        SouthIndex[2] = (n1 - 2) * n2 + tempj + 1;
        for (int k = 0; k < 3; k++)
            indices[n*3+l]=SouthIndex[0];
        indices[n*3+1+l]=SouthIndex[1];
        indices[n*3+2+l]=SouthIndex[2];
    }


    m_indexCount = sizeof indices / sizeof(indices[0]);

    m_func->glGenVertexArrays(1, &m_vao);
    m_func->glBindVertexArray(m_vao);

    m_indexBuf.bind();
    m_indexBuf.allocate(indices, sizeof indices);

    m_arrayBuf.bind();
    m_arrayBuf.allocate(vertices, sizeof vertices);

    const int positionLocation = 0;
    m_func->glEnableVertexAttribArray(positionLocation);
    m_func->glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, sizeof (VertexData), nullptr);

    const int texCoordLocation =  1;
    m_func->glEnableVertexAttribArray(texCoordLocation);
    m_func->glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void *)(sizeof (QVector3D)));

    m_func->glBindVertexArray(0);
}

void CSphere::initShader()
{
    const auto vShader = u8R"(#version 330 core
                         layout(location = 0) in vec3 position;
                         layout(location = 1) in vec2 texCoord;
                         uniform mat4 mvp;
                         out vec2 vTexCoord;
                         void main()
                         {
                         vTexCoord = texCoord;
                         gl_Position = mvp * vec4(position, 1.0);
                         }
                         )";
    const auto fShader = R"(#version 330 core
                         uniform sampler2D cubeTexture;
                         in vec2 vTexCoord;
                         out vec4 outColor;
                         void main()
                         {
                         outColor = texture(cubeTexture, vTexCoord);
                         }
                         )";
    if (!m_vertexShader.compileSourceCode(vShader)) {
        qWarning() << m_vertexShader.log();
    }
    if (!m_fragmentShader.compileSourceCode(fShader)) {
        qWarning() << m_fragmentShader.log();
    }
}
void CSphere::initTexture()
{
    QImage img(":/assets/mars.jpg");
    m_texture.setData(img.mirrored());
    m_texture.setMinificationFilter(QOpenGLTexture::Nearest);
    m_texture.setMagnificationFilter(QOpenGLTexture::Linear);
    m_texture.setWrapMode(QOpenGLTexture::Repeat);
}
void CSphere::draw(QOpenGLShaderProgram &program, const QMatrix4x4 &model, const QMatrix4x4 &view, const QMatrix4x4 &project)
{
    program.removeAllShaders();
    program.addShader(&m_vertexShader);
    program.addShader(&m_fragmentShader);
    if (!program.link()) {
        qWarning() << program.log();
        return;
    }
    if (!program.bind()) {
        qWarning() << program.log();
        return;
    }
    program.setUniformValue("mvp", project * view * model);

    m_texture.bind();
    program.setUniformValue("cubeTexture", 0);

    m_func->glBindVertexArray(m_vao);

    m_func->glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_SHORT, nullptr);
}

